"""
ZPL标签配置管理模块
用于加载和管理ZPL标签模板文件
"""

import os
import json
import re
from typing import Dict, Any, Optional, Union, List, Tuple, Pattern
from pathlib import Path
from ...utils.logger import get_logger
# 创建日志记录器
logger = get_logger("driver.device")

# 自定义异常类
class ZplLabelError(Exception):
    """ZPL标签处理相关的基础异常类"""
    pass

class TemplateNotFoundError(ZplLabelError):
    """模板未找到异常"""
    pass

class TemplateLoadError(ZplLabelError):
    """模板加载异常"""
    pass

class ContentReplacementError(ZplLabelError):
    """内容替换异常"""
    pass

# 类型别名
TemplateContent = str
ReplacementInfo = Dict[str, Any]

class ZplLabelConfigManager:
    """
    ZPL标签配置管理器
    负责加载、管理和更新ZPL标签模板文件

    功能特性:
    - 加载和缓存ZPL标签模板
    - 支持动态替换模板内容
    - 提供特殊字符转义功能
    - 支持文本、条码和二维码内容替换
    """

    # 正则表达式常量
    TEXT_CONTENT_PATTERN = r'(\^FD)(.*?)(\^FS)'
    BARCODE_CONTENT_PATTERN = r'(\^BC.*?\^FD)(.*?)(\^FS)'
    QRCODE_CONTENT_PATTERN = r'(\^BQ.*?\^FD)(.*?)(\^FS)'

    # 特殊字符转义映射
    COMMON_ESCAPE_MAP = {
        '^': '\\^',
        '~': '\\~',
        '&': '\\&',
        '%': '\\%',
        '$': '\\$',
        '#': '\\#',
        '@': '\\@',
        '!': '\\!',
        ':': '\\:',
        ';': '\\;',
        '"': '\\"',
        "'": "\\'",
        '<': '\\<',
        '>': '\\>',
        '=': '\\=',
        '?': '\\?',
        '`': '\\`',
        '|': '\\|',
        '{': '\\{',
        '}': '\\}',
        '[': '\\[',
        ']': '\\]'
    }

    BARCODE_RESERVED_CHARS = {'>'}  # 条码保留字符

    def __init__(self, labels: Dict[str, str]):
        """
        初始化ZPL标签配置管理器

        Args:
            labels (dict): 标签ID到标签文件名的映射

        Raises:
            TypeError: 当labels参数不是字典类型时抛出
        """
        logger.debug("初始化ZplLabelConfigManager")

        if not isinstance(labels, dict):
            error_msg = "labels参数必须是字典类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        self.labels = labels
        self.label_templates: Dict[str, str] = {}  # 存储加载的标签模板内容

        logger.info(f"配置了{len(labels)}个标签模板")
        # 加载标签模板
        self.load_label_templates()

    def load_label_templates(self) -> None:
        """
        根据配置加载所有标签模板文件到内存中

        Raises:
            TemplateLoadError: 当模板加载过程中出现错误时抛出
        """
        logger.debug(f"开始加载{len(self.labels)}个标签模板")

        loaded_count = 0
        for label_id, file_path in self.labels.items():
            try:
                logger.debug(f"加载标签模板: {label_id} -> {file_path}")

                if not isinstance(file_path, str):
                    error_msg = f"文件路径必须是字符串类型，当前类型为: {type(file_path)}"
                    logger.error(error_msg)
                    raise TypeError(error_msg)

                if os.path.exists(file_path):
                    with open(file_path, 'r', encoding='utf-8') as f:
                        self.label_templates[label_id] = f.read()
                        loaded_count += 1
                        logger.debug(f"成功加载标签模板: {label_id}")
                else:
                    error_msg = f"标签文件不存在: {file_path}"
                    logger.error(error_msg)
                    raise FileNotFoundError(error_msg)
            except FileNotFoundError as e:
                logger.error(f"文件未找到: {str(e)}")
                raise TemplateLoadError(str(e))
            except IOError as e:
                error_msg = f"读取标签文件失败 {file_path}: {str(e)}"
                logger.error(error_msg)
                raise TemplateLoadError(error_msg)
            except Exception as e:
                error_msg = f"加载标签文件时发生未知错误 {file_path}: {str(e)}"
                logger.error(error_msg)
                raise TemplateLoadError(error_msg)

        logger.info(f"成功加载{loaded_count}个标签模板")

    def _escape_special_chars(self, text: str) -> str:
        """
        转义ZPL语言中的特殊字符

        Args:
            text (str): 需要转义的原始文本

        Returns:
            str: 转义后的文本

        Raises:
            TypeError: 当text参数不是字符串类型时抛出
        """
        logger.debug(f"转义特殊字符: {text}")

        if not isinstance(text, str):
            error_msg = "text参数必须是字符串类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        result = self._escape_chars(text, self.COMMON_ESCAPE_MAP)
        logger.debug(f"转义完成: {result}")
        return result

    def _escape_barcode_special_chars(self, text: str) -> str:
        """
        转义条码内容中的特殊字符，保留条码功能字符

        特别注意：Code 128条码中的'>'字符用于切换代码集，不能被转义

        Args:
            text (str): 原始条码数据

        Returns:
            str: 处理后的条码数据

        Raises:
            TypeError: 当text参数不是字符串类型时抛出
        """
        logger.debug(f"转义条码特殊字符: {text}")

        if not isinstance(text, str):
            error_msg = "text参数必须是字符串类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        # 创建排除保留字符的转义映射
        barcode_escape_map = {
            char: escape
            for char, escape in self.COMMON_ESCAPE_MAP.items()
            if char not in self.BARCODE_RESERVED_CHARS
        }

        result = self._escape_chars(text, barcode_escape_map)
        logger.debug(f"条码转义完成: {result}")
        return result

    def _escape_chars(self, text: str, escape_map: Dict[str, str]) -> str:
        """
        通用字符转义方法

        Args:
            text (str): 原始文本
            escape_map (Dict[str, str]): 转义映射表

        Returns:
            str: 转义后的文本

        Raises:
            TypeError: 当参数类型不正确时抛出
        """
        logger.debug(f"通用字符转义，字符数: {len(text)}, 转义映射数: {len(escape_map)}")

        if not isinstance(text, str):
            error_msg = "text参数必须是字符串类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        if not isinstance(escape_map, dict):
            error_msg = "escape_map参数必须是字典类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        escaped_text = text
        for char, escaped_char in escape_map.items():
            escaped_text = escaped_text.replace(char, escaped_char)

        logger.debug(f"通用字符转义完成")
        return escaped_text

    def get_template(self, label_id: str, update_data: List[str] = None) -> Optional[str]:
        """
        获取指定标签ID的模板内容，并可选择性地按顺序替换内容

        Args:
            label_id (str): 标签ID
            update_data (List[str]): 新的数据内容，格式为["新内容1", "新内容2", ...]
                如果提供，则按顺序替换模板中的文本、条码、二维码内容

        Returns:
            str: 标签模板内容，如果未找到返回None

        Raises:
            TemplateNotFoundError: 当指定的标签模板不存在时抛出
            ContentReplacementError: 当内容替换过程中出现错误时抛出
            TypeError: 当参数类型不正确时抛出
        """
        logger.debug(f"获取模板: label_id={label_id}, update_data={update_data}")

        if not isinstance(label_id, str):
            error_msg = "label_id参数必须是字符串类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        if label_id not in self.label_templates:
            error_msg = f"标签模板未找到: {label_id}"
            logger.error(error_msg)
            raise TemplateNotFoundError(error_msg)

        template = self.label_templates[label_id]
        logger.debug(f"找到模板，长度: {len(template)} 字符")

        # 如果提供了update_data，则按顺序替换内容
        if update_data is not None:
            logger.debug(f"开始替换内容，数据项数: {len(update_data)}")

            if not isinstance(update_data, list):
                error_msg = "update_data参数必须是列表类型"
                logger.error(error_msg)
                raise TypeError(error_msg)

            try:
                # 收集所有需要替换的内容位置信息，按类型分组
                text_replacements = self._find_content_by_type(template, self.TEXT_CONTENT_PATTERN, 'text')
                barcode_replacements = self._find_content_by_type(template, self.BARCODE_CONTENT_PATTERN, 'barcode')
                qrcode_replacements = self._find_content_by_type(template, self.QRCODE_CONTENT_PATTERN, 'qrcode')

                # 按照在模板中出现的顺序合并所有替换项
                all_replacements = text_replacements + barcode_replacements + qrcode_replacements
                all_replacements.sort(key=lambda x: x['start'])
                logger.debug(f"找到 {len(all_replacements)} 个替换位置")

                # 按顺序执行替换 - 使用列表存储已处理的部分，避免位置偏移问题
                result_parts = []
                last_end = 0
                data_index = 0

                for replacement in all_replacements:
                    if data_index < len(update_data):
                        # 添加当前位置之前未处理的模板内容
                        result_parts.append(template[last_end:replacement['start']])

                        # 对于条码内容，使用专门的转义方法
                        if replacement['type'] == 'barcode':
                            new_value = self._escape_barcode_special_chars(update_data[data_index])
                        else:
                            new_value = self._escape_special_chars(update_data[data_index])

                        # 添加替换后的内容
                        new_content = f"{replacement['prefix']}{new_value}{replacement['suffix']}"
                        result_parts.append(new_content)
                        logger.debug(f"替换内容: '{replacement['full_match']}' -> '{new_content}'")

                        # 更新索引和数据计数
                        last_end = replacement['end']
                        data_index += 1

                # 添加剩余的模板内容
                result_parts.append(template[last_end:])

                # 组合最终结果
                template = ''.join(result_parts)
                logger.info(f"成功替换 {data_index} 个内容项")
            except TemplateNotFoundError:
                # 直接重新抛出已知异常
                raise
            except Exception as e:
                error_msg = f"替换模板内容时发生错误: {str(e)}"
                logger.error(error_msg)
                raise ContentReplacementError(error_msg)

        logger.info(f"成功获取并处理模板: {label_id}")
        return template

    def _find_content_by_type(self, template: str, pattern: str, content_type: str) -> List[ReplacementInfo]:
        """
        根据指定的正则表达式模式查找内容位置信息

        Args:
            template (str): ZPL模板内容
            pattern (str): 正则表达式模式
            content_type (str): 内容类型，如text/barcode/qrcode

        Returns:
            List[ReplacementInfo]: 包含位置信息的字典列表

        Raises:
            TypeError: 当参数类型不正确时抛出
            ValueError: 当参数值不合法时抛出
        """
        logger.debug(f"查找内容类型: {content_type}, 模式: {pattern}")

        if not isinstance(template, str):
            error_msg = "template参数必须是字符串类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        if not isinstance(pattern, str):
            error_msg = "pattern参数必须是字符串类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        if not isinstance(content_type, str):
            error_msg = "content_type参数必须是字符串类型"
            logger.error(error_msg)
            raise TypeError(error_msg)

        if not pattern:
            error_msg = "pattern参数不能为空"
            logger.error(error_msg)
            raise ValueError(error_msg)

        if not content_type:
            error_msg = "content_type参数不能为空"
            logger.error(error_msg)
            raise ValueError(error_msg)

        try:
            matches = list(re.finditer(pattern, template))
            logger.debug(f"找到 {len(matches)} 个匹配项")
        except re.error as e:
            error_msg = f"正则表达式模式错误: {str(e)}"
            logger.error(error_msg)
            raise ValueError(error_msg)

        replacements = []

        for match in matches:
            replacements.append({
                'start': match.start(),
                'end': match.end(),
                'full_match': match.group(),
                'prefix': match.group(1),
                'content': match.group(2),
                'suffix': match.group(3),
                'type': content_type
            })

        logger.debug(f"完成内容查找，返回 {len(replacements)} 个替换信息")
        return replacements